﻿using System.Collections.Generic;
using System.IO;
using System.Net;
using DBFile.Core.Interfaces;
using DBFile.Core.Web;

namespace DBFile.Providers.Impl
{
    internal class FtpClient : IDBFileProvider
    {
        private const int Success = 1;
        private const int Fail = 0;

        internal string Username { get; private set; }
        internal string Password { get; private set; }
        internal string RepositoryPath { get; private set; }

        internal FtpClient(string username, string password, string repositoryPath)
        {
            Username = username;
            Password = password;
            RepositoryPath = repositoryPath;
        }

        private FtpWebRequest CreateRequest(string target, string method)
        {
            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(RepositoryPath + target);
            request.Credentials = new NetworkCredential(Username, Password);
            request.UseBinary = true;
            request.Method = method;
            
            return request;
        }

        public int Copy(string sourceFilename, string destinationFilename, bool overwrite)
        {
            if (!overwrite && Exists(destinationFilename))
            {
                return Fail;
            }

            IDBFile file = Get(sourceFilename);
            return Insert(destinationFilename, file.FileBytes, file.ContentType, file.ContentLength);
        }

        public int Delete(string filename)
        {
            FtpWebRequest request = CreateRequest(filename, WebRequestMethods.Ftp.DeleteFile);
            FtpWebResponse response = null;
            try
            {
                response = (FtpWebResponse)request.GetResponse();
            }
            catch (WebException ex)
            {
                return Fail;
            }
            finally
            {
                if (response != null)
                {
                    response.Close();
                }
            }
            return Success;
        }

        public bool Exists(string filename)
        {
            return GetContentLength(filename) > -1;
        }
                
        public int Insert(string filename, byte[] buffer, string contentType, long contentLength)
        {
            FtpWebRequest request = CreateRequest(filename, WebRequestMethods.Ftp.UploadFile);
            request.KeepAlive = false;
            request.ContentLength = contentLength;
            try
            {
                Stream outStream = request.GetRequestStream();
                outStream.Write(buffer, 0, (int)contentLength);
                outStream.Close();
            }
            catch
            {
                return Fail;
            }
            return Success;
        }

        public int Insert(string filename, byte[] buffer, string contentType, long contentLength, bool overwrite)
        {
            if (!overwrite && Exists(filename))
            {
                return Fail;
            }

            return Insert(filename, buffer, contentType, contentLength);
        }

        public int Update(string filename, byte[] buffer, string contentType, long contentLength)
        {
            return Insert(filename, buffer, contentType, contentLength);
        }

        public HttpPostedFile Get(string filename)
        {
            return Get<HttpPostedFile>(filename);
        }

        private long GetContentLength(string filename)
        {
            FtpWebRequest request = CreateRequest(filename, WebRequestMethods.Ftp.GetFileSize);
            long contentLength = -1;
            try
            {
                FtpWebResponse response = (FtpWebResponse)request.GetResponse();

                using (Stream emptyStream = response.GetResponseStream())
                {
                    contentLength = response.ContentLength;
                }
                response.Close();
            }
            catch (WebException ex)
            { }
            return contentLength;
        }

        public T Get<T>(string filename) where T : IDBFile, new()
        {
            long contentLength = GetContentLength(filename);
            
            FtpWebRequest request = CreateRequest(filename, WebRequestMethods.Ftp.DownloadFile);
            FtpWebResponse response = (FtpWebResponse)request.GetResponse();
            T result = new T();

            using (Stream file = response.GetResponseStream())
            {
                result.ContentLength = contentLength;
                result.FileName = filename;
                result.ContentType = DBFile.Core.DBFile.GetContentType(Path.GetExtension(filename));
                result.FileBytes = new byte[contentLength];

                int bufferSize = 2048;
                int readCount = 0;
                int position = 0;

                readCount = file.Read(result.FileBytes, 0, bufferSize);
                position += readCount;
                while (readCount > 0)
                {
                    if ((position + bufferSize) > contentLength)
                    {
                        bufferSize = (int)contentLength - position;
                    }
                    readCount = file.Read(result.FileBytes, position, bufferSize);
                    position += readCount;
                }
                file.Close();
            }
            response.Close();

            return result;
        }

        public string[] GetFileNameList()
        {
            FtpWebRequest request = CreateRequest(string.Empty, WebRequestMethods.Ftp.ListDirectory);
            FtpWebResponse response = (FtpWebResponse)request.GetResponse();
            StreamReader reader = new StreamReader(response.GetResponseStream());

            var fileNames = new List<string>();
            try
            {
                string fileName = reader.ReadLine();
                while (fileName != null)
                {
                    fileNames.Add(fileName);
                    fileName = reader.ReadLine();
                }
            }
            finally
            {
                reader.Close();
                response.Close();
            }
            return fileNames.ToArray();
        }
    }
}
